package simple_http

import (
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"time"
)

const (
	HeadLen = 8
)

var reqRepChanMap = make(map[string]chan RepMsg)

type Router struct {
	paths map[string]func(ReqMsg) RepMsg
}

func NewRouter() *Router {
	return &Router{
		paths: make(map[string]func(ReqMsg) RepMsg),
	}
}

func (r *Router) AddRoute(path string, f func(ReqMsg) RepMsg) {
	r.paths[path] = f
}

func ReadPkg(reader io.Reader) (pkg []byte, err error) {
	head := make([]byte, HeadLen)
	mLen, err := reader.Read(head)
	if err != nil {
		return
	}
	if mLen != HeadLen {
		err = errors.New("解析包出错-头部长度错误1")
		return
	}
	contentLength := Bytes8ToInt(head)

	pkg = make([]byte, contentLength)
	mLen, err = reader.Read(pkg)
	if err != nil {
		return
	}
	if mLen != contentLength {
		err = errors.New("解析包体-长度错误")
		return
	}
	return
}

func WritePkg(writer io.Writer, msg Msg) (err error) {
	pkg, err := msg.Encode()
	if err != nil {
		return
	}

	head := IntToBytes8(len(pkg))
	_, err = writer.Write(head)
	if err != nil {
		return
	}

	_, err = writer.Write(pkg)
	return
}

func AcceptRequest(readWriter io.ReadWriter, router *Router) error {
	pkg, err := ReadPkg(readWriter)
	if err != nil {
		return err
	}

	msg, err := Decode(pkg)
	if err != nil {
		return err
	}

	switch msg.Type {
	case REQUEST:
		fun, ok := router.paths[msg.Path]
		if ok {
			go func() {
				repMsg := fun(msg.ReqMsg)
				repMsg.ReqId = msg.Id
				err = WritePkg(readWriter, Msg{
					Type:   RESPONSE,
					RepMsg: repMsg,
				})
				if err != nil {
					log.Fatalln(err)
				}
			}()
		} else {
			log.Println(msg.Path, " path is not found")
		}
	case RESPONSE:
		repMsgChan, ok := reqRepChanMap[msg.ReqId]
		if !ok {
			return errors.New(msg.ReqId + " request is not found")
		}
		repMsgChan <- msg.RepMsg
	}
	return nil
}

func CreateRequest(writer io.Writer, path string, params interface{}) (repMsg RepMsg, err error) {
	rand.Seed(time.Now().UnixNano())
	reqId := fmt.Sprintf("%d", rand.Uint64())
	msg := Msg{
		Type: REQUEST,
		ReqMsg: ReqMsg{
			Id:     reqId,
			Path:   path,
			Params: params,
		},
	}
	err = WritePkg(writer, msg)
	if err != nil {
		return
	}

	repMsgChan := make(chan RepMsg, 0)
	reqRepChanMap[reqId] = repMsgChan

	repMsg = <-repMsgChan
	return
}
